gtkdnd: Introduce a new API for more accurate drag origin data
authorJasper St. Pierre <jstpierre@mecheye.net>
Wed, 7 Aug 2013 09:10:42 +0000 (05:10 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Wed, 14 Aug 2013 11:12:52 +0000 (07:12 -0400)
When trying to drag, we currently the position of the first motion
event to determine where the drag came from. This might be alright
in the case of the old animation, but the data will be inaccurate
if the user has moved the pointer quite a bit since pressing the
cursor to start dragging. While we could monkey patch the GdkEvent
at the widget layer, this is unintuitive and strange.

Add a new API that takes a set of pointer coordinates describing
the origin of the drag. Additionally, adapt most widgets to use
it and use it with correct coordinates.

https://bugzilla.gnome.org/show_bug.cgi?id=705605

gtk/gtkcalendar.c
gtk/gtkdnd.c
gtk/gtkdnd.h
gtk/gtkentry.c
gtk/gtkiconview.c
gtk/gtklabel.c
gtk/gtknotebook.c
gtk/gtktextview.c
gtk/gtktreeview.c

index 2f0646e015d75fe37eba1ceaefb0f4355a73e2d0..81ed6e2324202f95107b9b2d8344b9aead561e24 100644 (file)
@@ -3058,8 +3058,9 @@ gtk_calendar_motion_notify (GtkWidget      *widget,
           GdkDragContext *context;
           GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
           gtk_target_list_add_text_targets (target_list, 0);
-          context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
-                                    1, (GdkEvent *)event);
+          context = gtk_drag_begin_with_coordinates (widget, target_list, GDK_ACTION_COPY,
+                                                     1, (GdkEvent *)event,
+                                                     priv->drag_start_x, priv->drag_start_y);
 
           priv->in_drag = 0;
           gtk_target_list_unref (target_list);
index 21ca429fdbf365c0db3a4a4793a8228130adb3b0..e7cb5ebfd6a70e67717dd31911893626493066f0 100644 (file)
@@ -2460,7 +2460,9 @@ gtk_drag_begin_internal (GtkWidget         *widget,
                         GtkTargetList     *target_list,
                         GdkDragAction      actions,
                         gint               button,
-                        GdkEvent          *event)
+                        GdkEvent          *event,
+                         int                x,
+                         int                y)
 {
   GtkDragSourceInfo *info;
   GList *targets = NULL;
@@ -2570,16 +2572,27 @@ gtk_drag_begin_internal (GtkWidget         *widget,
   /* Set cur_x, cur_y here so if the "drag-begin" signal shows
    * the drag icon, it will be in the right place
    */
-  if (event && event->type == GDK_MOTION_NOTIFY)
+  if (event)
+    info->cur_screen = gdk_event_get_screen (event);
+  else
+    gdk_device_get_position (pointer, &info->cur_screen, NULL, NULL);
+
+  if (x != -1 && y != -1)
     {
-      info->cur_screen = gtk_widget_get_screen (widget);
-      info->cur_x = event->motion.x_root;
-      info->cur_y = event->motion.y_root;
+      GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+      gtk_widget_translate_coordinates (widget, toplevel,
+                                        x, y, &x, &y);
+      gdk_window_get_root_coords (gtk_widget_get_window (toplevel),
+                                  x, y, &info->start_x, &info->start_y);
     }
-  else
+  else if (event && event->type == GDK_MOTION_NOTIFY)
     {
-      gdk_device_get_position (pointer, &info->cur_screen, &info->cur_x, &info->cur_y);
+      info->start_x = event->motion.x_root;
+      info->start_y = event->motion.y_root;
     }
+  else
+    gdk_device_get_position (pointer, NULL, &info->start_x, &info->start_y);
+
 
   g_signal_emit_by_name (widget, "drag-begin", info->context);
 
@@ -2610,15 +2623,15 @@ gtk_drag_begin_internal (GtkWidget         *widget,
           info->cursor = cursor;
         }
     }
-    
+
+  info->cur_x = info->start_x;
+  info->cur_y = info->start_y;
+
   if (event && event->type == GDK_MOTION_NOTIFY)
     gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
   else
     gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
 
-  info->start_x = info->cur_x;
-  info->start_y = info->cur_y;
-
   g_signal_connect (info->ipc_widget, "grab-broken-event",
                    G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
   g_signal_connect (info->ipc_widget, "grab-notify",
@@ -2641,13 +2654,19 @@ gtk_drag_begin_internal (GtkWidget         *widget,
 }
 
 /**
- * gtk_drag_begin: (method)
+ * gtk_drag_begin_with_coordinates: (method)
  * @widget: the source widget.
  * @targets: The targets (data formats) in which the
  *    source can provide the data.
  * @actions: A bitmask of the allowed drag actions for this drag.
  * @button: The button the user clicked to start the drag.
  * @event: The event that triggered the start of the drag.
+ * @x: The initial x coordinate to start dragging from, in the coordinate space
+ *    of @widget. If -1 is passed, the coordinates are retrieved from @event or
+ *    the current pointer position.
+ * @y: The initial y coordinate to start dragging from, in the coordinate space
+ *    of @widget. If -1 is passed, the coordinates are retrieved from @event or
+ *    the current pointer position.
  *
  * Initiates a drag on the source side. The function only needs to be used
  * when the application is starting drags itself, and is not needed when
@@ -2656,8 +2675,7 @@ gtk_drag_begin_internal (GtkWidget         *widget,
  * The @event is used to retrieve the timestamp that will be used internally to
  * grab the pointer.  If @event is %NULL, then %GDK_CURRENT_TIME will be used.
  * However, you should try to pass a real event in all cases, since that can be
- * used by GTK+ to get information about the start position of the drag, for
- * example if the @event is a motion event.
+ * used to get information about the drag.
  *
  * Generally there are three cases when you want to start a drag by hand by
  * calling this function:
@@ -2679,6 +2697,39 @@ gtk_drag_begin_internal (GtkWidget         *widget,
  * Return value: (transfer none): the context for this drag.
  **/
 GdkDragContext *
+gtk_drag_begin_with_coordinates (GtkWidget         *widget,
+                                 GtkTargetList     *targets,
+                                 GdkDragAction      actions,
+                                 gint               button,
+                                 GdkEvent          *event,
+                                 gint               x,
+                                 gint               y)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
+  g_return_val_if_fail (targets != NULL, NULL);
+
+  return gtk_drag_begin_internal (widget, NULL, targets,
+                                 actions, button, event, x, y);
+}
+
+/**
+ * gtk_drag_begin: (method)
+ * @widget: the source widget.
+ * @targets: The targets (data formats) in which the
+ *    source can provide the data.
+ * @actions: A bitmask of the allowed drag actions for this drag.
+ * @button: The button the user clicked to start the drag.
+ * @event: The event that triggered the start of the drag.
+ *
+ * This is equivalent to gtk_drag_begin_with_coordinates(), passing -1, -1
+ * as coordinates.
+ *
+ * Return value: (transfer none): the context for this drag.
+ *
+ * Deprecated: 3.10: Use gtk_drag_begin_with_coordinates() instead.
+ **/
+GdkDragContext *
 gtk_drag_begin (GtkWidget         *widget,
                GtkTargetList     *targets,
                GdkDragAction      actions,
@@ -2690,7 +2741,7 @@ gtk_drag_begin (GtkWidget         *widget,
   g_return_val_if_fail (targets != NULL, NULL);
 
   return gtk_drag_begin_internal (widget, NULL, targets,
-                                 actions, button, event);
+                                 actions, button, event, -1, -1);
 }
 
 /**
@@ -3872,8 +3923,8 @@ gtk_drag_source_event_cb (GtkWidget      *widget,
            {
              site->state = 0;
              gtk_drag_begin_internal (widget, site, site->target_list,
-                                      site->actions, 
-                                      i, event);
+                                      site->actions, i, event,
+                                       site->x, site->y);
 
              retval = TRUE;
            }
index 3ac12cd08adc84f55c9fe78ce818a5d3f7995d01..c3fc90f13a0eef953668cc48d60fedb027abf187 100644 (file)
@@ -187,7 +187,16 @@ void gtk_drag_source_set_icon_gicon   (GtkWidget       *widget,
  * as a GtkTargetList
  */
 
-GDK_AVAILABLE_IN_ALL
+GDK_AVAILABLE_IN_3_10
+GdkDragContext *gtk_drag_begin_with_coordinates (GtkWidget         *widget,
+                                                 GtkTargetList     *targets,
+                                                 GdkDragAction      actions,
+                                                 gint               button,
+                                                 GdkEvent          *event,
+                                                 gint               x,
+                                                 gint               y);
+
+GDK_DEPRECATED_IN_3_10_FOR(gtk_drag_begin_with_coordinates)
 GdkDragContext *gtk_drag_begin (GtkWidget         *widget,
                                GtkTargetList     *targets,
                                GdkDragAction      actions,
index 7e1bb3aadf650f6f4a2a9338818b8a60163edbe7..9283d8b87564113b4eb4747c5c12d20cc8893e28 100644 (file)
@@ -4435,11 +4435,13 @@ gtk_entry_motion_notify (GtkWidget      *widget,
             {
               icon_info->in_drag = TRUE;
               icon_info->pressed = FALSE;
-              gtk_drag_begin (widget,
-                              icon_info->target_list,
-                              icon_info->actions,
-                              1,
-                              (GdkEvent*)event);
+              gtk_drag_begin_with_coordinates (widget,
+                                               icon_info->target_list,
+                                               icon_info->actions,
+                                               1,
+                                               (GdkEvent*)event,
+                                               priv->start_x,
+                                               priv->start_y);
             }
 
           return TRUE;
@@ -4471,6 +4473,8 @@ gtk_entry_motion_notify (GtkWidget      *widget,
                                     priv->drag_start_x, priv->drag_start_y,
                                     event->x + priv->scroll_offset, event->y))
         {
+          gint *ranges;
+          gint n_ranges;
           GdkDragContext *context;
           GtkTargetList  *target_list = gtk_target_list_new (NULL, 0);
           guint actions = priv->editable ? GDK_ACTION_COPY | GDK_ACTION_MOVE : GDK_ACTION_COPY;
@@ -4482,9 +4486,17 @@ gtk_entry_motion_notify (GtkWidget      *widget,
           text = _gtk_entry_get_selected_text (entry);
           surface = _gtk_text_util_create_drag_icon (widget, text, -1);
 
-          context = gtk_drag_begin (widget, target_list, actions,
-                                    priv->button, (GdkEvent *)event);
-          
+          gtk_entry_get_pixel_ranges (entry, &ranges, &n_ranges);
+          cairo_surface_set_device_offset (surface,
+                                           -(priv->drag_start_x - ranges[0]),
+                                           -(priv->drag_start_y));
+
+          context = gtk_drag_begin_with_coordinates (widget, target_list, actions,
+                                                     priv->button, (GdkEvent *)event,
+                                                     priv->drag_start_x + ranges[0],
+                                                     priv->drag_start_y);
+          g_free (ranges);
+
           if (surface)
             gtk_drag_set_icon_surface (context, surface);
           else
index 05249792c67f7db5a6ce8515cd1ad805a9046a18..070378ab7bda03f439d5c73fee2c521bb3391b7d 100644 (file)
@@ -6446,11 +6446,13 @@ gtk_icon_view_maybe_begin_drag (GtkIconView    *icon_view,
   
   retval = TRUE;
 
-  context = gtk_drag_begin (widget,
-                            gtk_drag_source_get_target_list (widget),
-                            icon_view->priv->source_actions,
-                            button,
-                            (GdkEvent*)event);
+  context = gtk_drag_begin_with_coordinates (widget,
+                                             gtk_drag_source_get_target_list (widget),
+                                             icon_view->priv->source_actions,
+                                             button,
+                                             (GdkEvent*)event,
+                                             icon_view->priv->press_start_x,
+                                             icon_view->priv->press_start_y);
 
   set_source_row (context, model, path);
   
index 4d13cacd0024095a5d0fed74475fd8405058426b..f8abece13f09edd1379654d8fd11a5dd8348427e 100644 (file)
@@ -4944,9 +4944,11 @@ gtk_label_motion (GtkWidget      *widget,
 
           g_signal_connect (widget, "drag-begin",
                             G_CALLBACK (drag_begin_cb), NULL);
-         gtk_drag_begin (widget, target_list,
-                         GDK_ACTION_COPY,
-                         1, (GdkEvent *)event);
+         gtk_drag_begin_with_coordinates (widget, target_list,
+                                           GDK_ACTION_COPY,
+                                           1, (GdkEvent *)event,
+                                           info->drag_start_x,
+                                           info->drag_start_y);
 
          info->in_drag = FALSE;
 
index b44c84aac6eb02f7ad4ea1d01237558a3675a9fa..48a05802966d6d345da239c329f079295e8dd3c6 100644 (file)
@@ -3415,8 +3415,9 @@ gtk_notebook_motion_notify (GtkWidget      *widget,
       priv->detached_tab = priv->cur_page;
       priv->during_detach = TRUE;
 
-      gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
-                      priv->pressed_button, (GdkEvent*) event);
+      gtk_drag_begin_with_coordinates (widget, priv->source_targets, GDK_ACTION_MOVE,
+                                       priv->pressed_button, (GdkEvent*) event,
+                                       priv->drag_begin_x, priv->drag_begin_y);
       return TRUE;
     }
 
index bbfa166e43223db5a94b378e9b0a704486681289..3aa1047ac3f0a499764cdf6bb06239b9f632c50a 100644 (file)
@@ -7291,17 +7291,19 @@ gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
 {
   GtkTargetList *target_list;
 
-  text_view->priv->drag_start_x = -1;
-  text_view->priv->drag_start_y = -1;
-  text_view->priv->pending_place_cursor_button = 0;
-
   target_list = gtk_text_buffer_get_copy_target_list (get_buffer (text_view));
 
   g_signal_connect (text_view, "drag-begin",
                     G_CALLBACK (drag_begin_cb), NULL);
-  gtk_drag_begin (GTK_WIDGET (text_view), target_list,
-                 GDK_ACTION_COPY | GDK_ACTION_MOVE,
-                 1, (GdkEvent*)event);
+  gtk_drag_begin_with_coordinates (GTK_WIDGET (text_view), target_list,
+                                   GDK_ACTION_COPY | GDK_ACTION_MOVE,
+                                   1, (GdkEvent*)event,
+                                   text_view->priv->drag_start_x,
+                                   text_view->priv->drag_start_y);
+
+  text_view->priv->drag_start_x = -1;
+  text_view->priv->drag_start_y = -1;
+  text_view->priv->pending_place_cursor_button = 0;
 }
 
 static void
index b9c2bb5b8b21ab1fb49f612c88f79e9e73f25a00..718174c47fcdeb02dbf0f77dc9326cf1d72f7eca 100644 (file)
@@ -7570,6 +7570,7 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView      *tree_view,
   GtkTreePath *path = NULL;
   gint button;
   gint cell_x, cell_y;
+  gint drag_start_x, drag_start_y;
   GtkTreeModel *model;
   gboolean retval = FALSE;
 
@@ -7618,11 +7619,17 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView      *tree_view,
 
   retval = TRUE;
 
-  context = gtk_drag_begin (widget,
-                            gtk_drag_source_get_target_list (widget),
-                            di->source_actions,
-                            button,
-                            (GdkEvent*)event);
+  gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
+                                                     tree_view->priv->press_start_x,
+                                                     tree_view->priv->press_start_y,
+                                                     &drag_start_x, &drag_start_y);
+
+  context = gtk_drag_begin_with_coordinates (widget,
+                                             gtk_drag_source_get_target_list (widget),
+                                             di->source_actions,
+                                             button,
+                                             (GdkEvent*)event,
+                                             drag_start_x, drag_start_y);
 
   set_source_row (context, model, path);